﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Charades.Web.Helpers;
using Charades.Web.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;

namespace Charades.Web.Controllers
{
	[Authorize]
	[AccountSecurity]
	public class AccountController : Controller
	{
		public AccountController()
			: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
		{
		}

		public AccountController(UserManager<ApplicationUser> userManager)
		{
			UserManager = userManager;
		}

		public UserManager<ApplicationUser> UserManager { get; private set; }

		[AllowAnonymous]
		public ActionResult Login(string provider)
		{
			if (AuthenticationManager.GetExternalAuthenticationTypes().Any(x => x.AuthenticationType == provider))
			{
				return View(model: provider);
			}
			return View("LoginFailure");
		}

		//
		// POST: /Account/ExternalLogin
		[HttpPost]
		[AllowAnonymous]
		[ValidateAntiForgeryToken]
		public ActionResult ExternalLogin(string provider)
		{
			// Request a redirect to the external login provider
			return new ChallengeResult(provider, Url.Action("LoginCallback", "Account"));
		}

		//
		// GET: /Account/ExternalLoginCallback
		[AllowAnonymous]
		public async Task<ActionResult> LoginCallback()
		{
			try
			{
				var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
				if (loginInfo != null)
				{
					var user = await UserManager.FindAsync(loginInfo.Login);
					if (user != null)
					{
						await SignInAsync(user, isPersistent: false);
						return View("LoginSuccess");
					}

					user = new ApplicationUser()
					{
						UserName = loginInfo.DefaultUserName,
						Email = string.IsNullOrEmpty(loginInfo.Email) ?
									loginInfo.ExternalIdentity.FindFirstValue(ClaimTypes.Email) :
									loginInfo.Email,
						EmailConfirmed = true,
						DisplayName = loginInfo.DefaultUserName,
						Color = ColorHelper.GenerateColor()
					};

					if (await UserManager.FindByNameAsync(user.UserName) != null)
					{
						user.UserName += RandomNameHelper.GetName(5);
					}

					var result = await UserManager.CreateAsync(user);
					if (result.Succeeded)
					{
						result = await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
						if (result.Succeeded)
						{
							await SignInAsync(user, isPersistent: false);
							Request.Cookies.Set(new HttpCookie("Welcome"));
							return View("LoginSuccess");
						}
					}
				}
			}
			catch (Exception e)
			{
				ElmahLogger.LogMessage(e.Message);
			}

			return View("LoginFailure");
		}

		//
		// POST: /Account/LogOff
		[HttpPost]
		[ValidateAntiForgeryToken]
		public ActionResult LogOff()
		{
			AuthenticationManager.SignOut();
			return RedirectToAction("Index", "Home");
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing && UserManager != null)
			{
				UserManager.Dispose();
				UserManager = null;
			}
			base.Dispose(disposing);
		}

		#region Helpers
		// Used for XSRF protection when adding external logins
		private const string XsrfKey = "XsrfId";

		private IAuthenticationManager AuthenticationManager
		{
			get
			{
				return HttpContext.GetOwinContext().Authentication;
			}
		}

		private async Task SignInAsync(ApplicationUser user, bool isPersistent)
		{
			AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
			var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
			AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
		}

		private class ChallengeResult : HttpUnauthorizedResult
		{
			public ChallengeResult(string provider, string redirectUri)
				: this(provider, redirectUri, null)
			{
			}

			public ChallengeResult(string provider, string redirectUri, string userId)
			{
				LoginProvider = provider;
				RedirectUri = redirectUri;
				UserId = userId;
			}

			public string LoginProvider { get; set; }
			public string RedirectUri { get; set; }
			public string UserId { get; set; }

			public override void ExecuteResult(ControllerContext context)
			{
				var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
				if (UserId != null)
				{
					properties.Dictionary[XsrfKey] = UserId;
				}
				context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
			}
		}
		#endregion
	}
}